package editor.modelo;



import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import server.buscador.SpaceNode;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import editor.modelo.LandMark.Tipo;
import editor.modelo.Portal.Clase;


public class JSONConverter {

	private int type = 0;
	private String floorName;
	private String buildingName;


	public JSONConverter(Class<? extends Floor> c){
		if(c.equals(EditorFloor.class)){
			this.type = 1;
		}
		else if(c.equals(Floor.class))
			type = 2;
	}

	public JSONConverter(int i) {
		type = i;
	}

///////////////////////////////TO JSON //////////////////////////////////////////////
	public String toJSONFile(Floor piso, String path){
		String json="";
		try {
			FileWriter writer = new FileWriter(path);
			json = toJSONString(piso);
			writer.write(json);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return json;
	}

	public String toJSONString(Floor piso){
		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		return  gson.toJson(toJSON(piso));
	}

	private Formato toJSON(Floor piso){
		Formato formato = new Formato();
		_Floor miPiso = toJSONFloor(piso.getFloorSpace());
		miPiso.level = piso.getLevel();
		miPiso.absoluteHeight = piso.getAlturaAbsouta();
		if(piso.getReferencia() != null)
			miPiso.referenceNode = piso.getReferencia().getName();

		ArrayList<_Space> l = new ArrayList<_Space>();
		for(Space sp: piso.getEspacios()){
			l.add(toJSONSpace(sp));
		}
		formato.spaces = (_Space[])l.toArray(new _Space[0]);
		
		ArrayList<_Portal> m = new ArrayList<_Portal>();
		for(Portal po: piso.getPortales()){
			m.add(toJSONPortal(po));
		}
		formato.portals = (_Portal[])m.toArray(new _Portal[0]);
		
		ArrayList<_LandMark> lm = new ArrayList<_LandMark>();
		for(LandMark land: piso.getLandmarks()){
			lm.add(toJSONPortal(land));
		}
		formato.landmarks = (_LandMark[])lm.toArray(new _LandMark[0]);
		
		formato.floor = miPiso;
		return formato;
	}

	private _LandMark toJSONPortal(LandMark land) {
		_LandMark lm = new _LandMark();
		_PuntoXY p = new _PuntoXY();
		p.x = (float) land.getCentro().getX();
		p.y = (float) land.getCentro().getY();
		lm.centroid = p;
		lm.name = land.getName();
		lm.type = land.getTipo().toString();
		lm.parent = land.getSpace().getName();
		
		return lm;
	}

	private _Floor toJSONFloor(Space s){
		_Floor floor = new _Floor();
		floor.name= s.getName();
		_Contour c = new _Contour();
		_PuntoXY p = new _PuntoXY();
		p.x = 2; p.y = 1;
		c.centroid = p;
		c.points = new _PuntoXY[]{p,p,p,p,p};
		floor.contour = c;
		return floor;
	}
	
	private _Space toJSONSpace(Space s){
		_Space sp = new _Space();
		_Contour contorno = new _Contour();
		_PuntoXY centroide = new _PuntoXY();
		ArrayList<_PuntoXY> l = new ArrayList<_PuntoXY>();
		_PuntoXY ptoJ;
		for(Point2D pto: s.getBorde()){
			ptoJ = new _PuntoXY();
			ptoJ.x= (float)pto.getX();
			ptoJ.y= (float)pto.getY();
			l.add(ptoJ);
		}
		centroide.x = (float)s.getCentro().getX();
		centroide.y = (float)s.getCentro().getY();
		contorno.centroid = centroide;
		contorno.points = (_PuntoXY[])l.toArray( new _PuntoXY[0]);
		sp.name = s.getName();
		sp.type = s.getTipo().toString();
		sp.contour = contorno;
		if(s.height != null)
			sp.height = s.getHeight();
		else sp.height = 0;
		return sp;
	}

	private _Portal toJSONPortal(Portal p){
		_Portal portal = new _Portal();
		portal.bidirectional = p.isBidirectional();
		portal.class_= p.getClase().toString();
		portal.type = p.getTipo().toString();
		portal.differential = p.getDifferential();
		portal.sourceID = p.getSource().getName();
		portal.sourcePointIndex = p.getSource().getBorde().indexOf(p.getPtoSource());
		//		{
		if(p.getTarget()!= null){
			if(p.getClase() == Clase.intra_floor){ //si es del mismo piso va el nombre simple
				portal.targetID = p.getTarget().getName();
			}
			else{
				portal.targetID = p.getTarget().getNombreCompleto();
			}
			portal.targetPointIndex = p.getTarget().getBorde().indexOf(p.getPtoTarget());
		}
		return portal;
	}

	
///////////////////////////////TO JSON //////////////////////////////////////////////
//////////////////////////////FROM JSON //////////////////////////////////////////////

	public Floor fromJSONFile(String path){
		File archivo = new File(path);
		int ind = archivo.getName().lastIndexOf(('.'));
		floorName = archivo.getName().substring(0, ind); //le saco el .json
		buildingName = archivo.getParentFile().getName();
		Floor f =  getNewFloor();
		f.setbuildingName(buildingName);
		try {
			BufferedReader br = new BufferedReader(
					new FileReader(archivo));
			String s ="";
			while(br.ready()){
				s+= br.readLine();
			}
			br.close();
			loadFloorfromJSONString(s, f);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return f;

	}

	public void loadFloorfromJSONString(String json, Floor f){
		Gson gson = new Gson();
		f.inicializar();
		Formato obj = gson.fromJson(json, Formato.class);
	
		f.setLevel(obj.floor.level);
		f.setAlturaAbsouta(obj.floor.absoluteHeight);
		
		for(_Space sp: obj.spaces){
			Space space = fromJSONSpace(sp);
			if(obj.floor.referenceNode != null)
			if(obj.floor.referenceNode.equals(sp.name)){
				f.setReferencia(space);
			}
			f.getEspacios().add(space);
		}
		for(_Portal po: obj.portals){
			f.getPortales().add(formJSONPortal(po, f.getEspacios()));
		}
		if(obj.landmarks != null)
		for(_LandMark lm: obj.landmarks){
			f.getLandmarks().add(fromJSONLandmark(lm, f.getEspacios()));
		}
		Space spaceDelPiso = formJSONFloor(obj.floor);
		f.setFloorSpace(spaceDelPiso);
	}
	
	private LandMark fromJSONLandmark(_LandMark lm, List<Space> espacios) {
		LandMark nlm = getLandMark();
		nlm.setName(lm.name);
		nlm.setCentro(new Point2D.Float(lm.centroid.x, lm.centroid.y));
		nlm.setTipo(LandMark.Tipo.valueOf(lm.type));
		nlm.setParent(findSpacebyName(espacios, lm.parent));
		return nlm;
	}



	private Space formJSONFloor(_Floor f){
		Space sp = getNewSpace();
		sp.setName(f.name);
		sp.setNombrePadreCompleto(buildingName+"/"+floorName);
		//sp.setTipo(Space.Tipo.valueOf(s.type));
		sp.setCentro(new Point2D.Float(f.contour.centroid.x, f.contour.centroid.y));
		for(_PuntoXY p: f.contour.points){
			sp.getBorde().add(new Point2D.Float(p.x, p.y));
		}
		return sp;
	}

	private Space fromJSONSpace(_Space s){
		Space sp = getNewSpace();
		sp.setName(s.name);
		sp.setNombrePadreCompleto(buildingName+"/"+floorName);
		sp.setTipo(Space.Tipo.valueOf(s.type));
		sp.setHeight(s.height);
		sp.setCentro(new Point2D.Float(s.contour.centroid.x,s.contour.centroid.y));
		for(_PuntoXY p: s.contour.points){
			sp.getBorde().add(new Point2D.Float(p.x, p.y));
		}
		return sp;
	}

	
	private Space findSpacebyName(List<Space> l, String name){
		for(Space s:l){
			if(s.getName().equals(name)) return s;
		}
		return null;
	}

	/**
	 * 
	 * @param p
	 * @param spaces: para convertir la referencia
	 * @return
	 */
	private Portal formJSONPortal(_Portal p, List<Space> spaces){
		Portal portal = getNewPortal();
		portal.setBidirectional(p.bidirectional);
		portal.setClase(Portal.Clase.valueOf(p.class_));
		portal.setTipo(Portal.Tipo.valueOf(p.type));
		portal.setDifferential(p.differential);
		portal.setSource(findSpacebyName(spaces, p.sourceID));
		portal.setPtoSource(portal.getSource().getBorde().get(p.sourcePointIndex));
		//	if(portal.getClase()== Clase.intra_floor){
		//si el portal es externo no va a linkear. No puede porque es un mapa solo.
		portal.setTarget(findSpacebyName(spaces, p.targetID));
		if(portal.getTarget()!= null) //pued no estar linkeado
			portal.setPtoTarget(portal.getTarget().getBorde().get(p.targetPointIndex));
		//}
		return portal;
	}
	
//////////////////////////////FROM JSON //////////////////////////////////////////////
	
	/* Estas clases se corresponen con como se exporta al JSON*/
	public class Formato{
		public _Floor floor;
		public _Space[] spaces;
		public _Portal[] portals;
		public _LandMark[] landmarks;

	}
	public class _Floor{
		public String name;
		public String referenceNode;
		public int absoluteHeight;
		public int level;
		public _Contour contour; 
	}
	
	public class _Space{
		public String name;
		public String type;
		public int height;
		public _Contour contour;

	}
	public class _Portal{
		public String class_;
		public String type;
		public String sourceID;
		public String targetID;

		public boolean bidirectional;
		public int sourcePointIndex;
		public int targetPointIndex;
		public int differential;
	}
	
	public class _LandMark{
		protected String type;
		protected String name;
		protected _PuntoXY centroid;
		protected String parent;
	}

	public class _Contour{
		public _PuntoXY centroid;
		public _PuntoXY[] points;
	}

	public class _PuntoXY{
		public float x;
		public float y;
	}
	

	private Floor getNewFloor(){
		if(type == 1) return new EditorFloor();
		if(type == 2) return new Floor();
		if(type == 3) return new Floor();
		return null;
	}
	private Space getNewSpace() {
		if(type == 1) return new EditorSpace();
		if(type == 2) return new Space();
		if(type == 3) return new SpaceNode();
		return null;
	}
	private Portal getNewPortal(){
		if(type == 1) return new EditorPortal();
		if(type == 2) return new Portal();
		if(type == 3) return new Portal();
		return null;
	}
	
	private LandMark getLandMark() {
		if(type == 1) return new EditorLandMark();
		if(type == 2) return new LandMark();
		if(type == 3) return new LandMark();
		return null;
	}

}

